/*
 * GiST 对 ltree 的支持
 * Teodor Sigaev <teodor@stack.net>
 * contrib/ltree/ltree_gist.c
 */
#include "postgres.h"

#include "access/gist.h"
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "crc32.h"
#include "ltree.h"

#define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
#define ISEQ(a,b)	( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )

PG_FUNCTION_INFO_V1(ltree_gist_in);
PG_FUNCTION_INFO_V1(ltree_gist_out);

Datum ltree_gist_in(PG_FUNCTION_ARGS)
{
	ereport(ERROR,
			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
			 errmsg("ltree_gist_in() not implemented")));
	PG_RETURN_DATUM(0);
}

Datum ltree_gist_out(PG_FUNCTION_ARGS)
{
	ereport(ERROR,
			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
			 errmsg("ltree_gist_out() not implemented")));
	PG_RETURN_DATUM(0);
}

ltree_gist *
ltree_gist_alloc(bool fc_isalltrue, BITVECP fc_sign, int fc_siglen,
				 ltree *fc_left, ltree *fc_right)
{
	int32		fc_size = LTG_HDRSIZE + (fc_isalltrue ? 0 : fc_siglen) +
	(fc_left ? VARSIZE(fc_left) + (fc_right ? VARSIZE(fc_right) : 0) : 0);
	ltree_gist *fc_result = palloc(fc_size);

	SET_VARSIZE(fc_result, fc_size);

	if (fc_siglen)
	{
		fc_result->flag = 0;

		if (fc_isalltrue)
			fc_result->flag |= LTG_ALLTRUE;
		else if (fc_sign)
			memcpy(LTG_SIGN(fc_result), fc_sign, fc_siglen);
		else
			memset(LTG_SIGN(fc_result), 0, fc_siglen);

		if (fc_left)
		{
			memcpy(LTG_LNODE(fc_result, fc_siglen), fc_left, VARSIZE(fc_left));

			if (!fc_right || fc_left == fc_right || ISEQ(fc_left, fc_right))
				fc_result->flag |= LTG_NORIGHT;
			else
				memcpy(LTG_RNODE(fc_result, fc_siglen), fc_right, VARSIZE(fc_right));
		}
	}
	else
	{
		Assert(fc_left);
		fc_result->flag = LTG_ONENODE;
		memcpy(LTG_NODE(fc_result), fc_left, VARSIZE(fc_left));
	}

	return fc_result;
}

PG_FUNCTION_INFO_V1(ltree_compress);
PG_FUNCTION_INFO_V1(ltree_decompress);
PG_FUNCTION_INFO_V1(ltree_same);
PG_FUNCTION_INFO_V1(ltree_union);
PG_FUNCTION_INFO_V1(ltree_penalty);
PG_FUNCTION_INFO_V1(ltree_picksplit);
PG_FUNCTION_INFO_V1(ltree_consistent);
PG_FUNCTION_INFO_V1(ltree_gist_options);

#define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))

Datum ltree_compress(PG_FUNCTION_ARGS)
{
	GISTENTRY  *fc_entry = (GISTENTRY *) PG_GETARG_POINTER(0);
	GISTENTRY  *fc_retval = fc_entry;

	if (fc_entry->leafkey)
	{							/* ltree */
		ltree	   *fc_val = DatumGetLtreeP(fc_entry->key);
		ltree_gist *fc_key = ltree_gist_alloc(false, NULL, 0, fc_val, 0);

		fc_retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
		gistentryinit(*fc_retval, PointerGetDatum(fc_key),
					  fc_entry->rel, fc_entry->page,
					  fc_entry->offset, false);
	}
	PG_RETURN_POINTER(fc_retval);
}

Datum ltree_decompress(PG_FUNCTION_ARGS)
{
	GISTENTRY  *fc_entry = (GISTENTRY *) PG_GETARG_POINTER(0);
	ltree_gist *fc_key = (ltree_gist *) PG_DETOAST_DATUM(fc_entry->key);

	if (PointerGetDatum(fc_key) != fc_entry->key)
	{
		GISTENTRY  *fc_retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));

		gistentryinit(*fc_retval, PointerGetDatum(fc_key),
					  fc_entry->rel, fc_entry->page,
					  fc_entry->offset, false);
		PG_RETURN_POINTER(fc_retval);
	}
	PG_RETURN_POINTER(fc_entry);
}

Datum ltree_same(PG_FUNCTION_ARGS)
{
	ltree_gist *fc_a = (ltree_gist *) PG_GETARG_POINTER(0);
	ltree_gist *fc_b = (ltree_gist *) PG_GETARG_POINTER(1);
	bool	   *fc_result = (bool *) PG_GETARG_POINTER(2);
	int			fc_siglen = LTREE_GET_SIGLEN();

	*fc_result = false;
	if (LTG_ISONENODE(fc_a) != LTG_ISONENODE(fc_b))
		PG_RETURN_POINTER(fc_result);

	if (LTG_ISONENODE(fc_a))
		*fc_result = ISEQ(LTG_NODE(fc_a), LTG_NODE(fc_b));
	else
	{
		int32		i;
		BITVECP		fc_sa = LTG_SIGN(fc_a),
					fc_sb = LTG_SIGN(fc_b);

		if (LTG_ISALLTRUE(fc_a) != LTG_ISALLTRUE(fc_b))
			PG_RETURN_POINTER(fc_result);

		if (!ISEQ(LTG_LNODE(fc_a, fc_siglen), LTG_LNODE(fc_b, fc_siglen)))
			PG_RETURN_POINTER(fc_result);
		if (!ISEQ(LTG_RNODE(fc_a, fc_siglen), LTG_RNODE(fc_b, fc_siglen)))
			PG_RETURN_POINTER(fc_result);

		*fc_result = true;
		if (!LTG_ISALLTRUE(fc_a))
		{
			LOOPBYTE(fc_siglen)
			{
				if (fc_sa[i] != fc_sb[i])
				{
					*fc_result = false;
					break;
				}
			}
		}
	}

	PG_RETURN_POINTER(fc_result);
}

static void fc_hashing(BITVECP fc_sign, ltree *fc_t, int fc_siglen)
{
	int			fc_tlen = fc_t->numlevel;
	ltree_level *fc_cur = LTREE_FIRST(fc_t);
	int			fc_hash;

	while (fc_tlen > 0)
	{
		fc_hash = ltree_crc32_sz(fc_cur->name, fc_cur->len);
		HASH(fc_sign, fc_hash, fc_siglen);
		fc_cur = LEVEL_NEXT(fc_cur);
		fc_tlen--;
	}
}

Datum ltree_union(PG_FUNCTION_ARGS)
{
	GistEntryVector *fc_entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
	int		   *fc_size = (int *) PG_GETARG_POINTER(1);
	int			fc_siglen = LTREE_GET_SIGLEN();
	BITVECP		fc_base = palloc0(fc_siglen);
	int32		i,
				fc_j;
	ltree_gist *fc_result,
			   *fc_cur;
	ltree	   *fc_left = NULL,
			   *fc_right = NULL,
			   *fc_curtree;
	bool		fc_isalltrue = false;

	for (fc_j = 0; fc_j < fc_entryvec->n; fc_j++)
	{
		fc_cur = GETENTRY(fc_entryvec, fc_j);
		if (LTG_ISONENODE(fc_cur))
		{
			fc_curtree = LTG_NODE(fc_cur);
			fc_hashing(fc_base, fc_curtree, fc_siglen);
			if (!fc_left || ltree_compare(fc_left, fc_curtree) > 0)
				fc_left = fc_curtree;
			if (!fc_right || ltree_compare(fc_right, fc_curtree) < 0)
				fc_right = fc_curtree;
		}
		else
		{
			if (fc_isalltrue || LTG_ISALLTRUE(fc_cur))
				fc_isalltrue = true;
			else
			{
				BITVECP		fc_sc = LTG_SIGN(fc_cur);

				LOOPBYTE(fc_siglen)
					((unsigned char *) fc_base)[i] |= fc_sc[i];
			}

			fc_curtree = LTG_LNODE(fc_cur, fc_siglen);
			if (!fc_left || ltree_compare(fc_left, fc_curtree) > 0)
				fc_left = fc_curtree;
			fc_curtree = LTG_RNODE(fc_cur, fc_siglen);
			if (!fc_right || ltree_compare(fc_right, fc_curtree) < 0)
				fc_right = fc_curtree;
		}
	}

	if (fc_isalltrue == false)
	{
		fc_isalltrue = true;
		LOOPBYTE(fc_siglen)
		{
			if (((unsigned char *) fc_base)[i] != 0xff)
			{
				fc_isalltrue = false;
				break;
			}
		}
	}

	fc_result = ltree_gist_alloc(fc_isalltrue, fc_base, fc_siglen, fc_left, fc_right);

	*fc_size = VARSIZE(fc_result);

	PG_RETURN_POINTER(fc_result);
}

Datum ltree_penalty(PG_FUNCTION_ARGS)
{
	ltree_gist *fc_origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
	ltree_gist *fc_newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
	float	   *fc_penalty = (float *) PG_GETARG_POINTER(2);
	int			fc_siglen = LTREE_GET_SIGLEN();
	int32		fc_cmpr,
				fc_cmpl;

	fc_cmpl = ltree_compare(LTG_GETLNODE(fc_origval, fc_siglen), LTG_GETLNODE(fc_newval, fc_siglen));
	fc_cmpr = ltree_compare(LTG_GETRNODE(fc_newval, fc_siglen), LTG_GETRNODE(fc_origval, fc_siglen));

	*fc_penalty = Max(fc_cmpl, 0) + Max(fc_cmpr, 0);

	PG_RETURN_POINTER(fc_penalty);
}

/* 用于排序 */
typedef struct rix
{
	int			index;
	ltree	   *r;
} RIX;

static int fc_treekey_cmp(const void *fc_a, const void *fc_b)
{
	return ltree_compare(((const RIX *) fc_a)->r,
						 ((const RIX *) fc_b)->r);
}


Datum ltree_picksplit(PG_FUNCTION_ARGS)
{
	GistEntryVector *fc_entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
	GIST_SPLITVEC *fc_v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
	int			fc_siglen = LTREE_GET_SIGLEN();
	OffsetNumber fc_j;
	int32		i;
	RIX		   *fc_array;
	OffsetNumber fc_maxoff;
	int			fc_nbytes;
	ltree	   *fc_lu_l,
			   *fc_lu_r,
			   *fc_ru_l,
			   *fc_ru_r;
	ltree_gist *fc_lu,
			   *fc_ru;
	BITVECP		fc_ls = palloc0(fc_siglen),
				fc_rs = palloc0(fc_siglen);
	bool		fc_lisat = false,
				fc_risat = false;

	fc_maxoff = fc_entryvec->n - 1;
	fc_nbytes = (fc_maxoff + 2) * sizeof(OffsetNumber);
	fc_v->spl_left = (OffsetNumber *) palloc(fc_nbytes);
	fc_v->spl_right = (OffsetNumber *) palloc(fc_nbytes);
	fc_v->spl_nleft = 0;
	fc_v->spl_nright = 0;
	fc_array = (RIX *) palloc(sizeof(RIX) * (fc_maxoff + 1));

	/* 将数据复制到 RIX，并对 RIX 进行排序 */
	for (fc_j = FirstOffsetNumber; fc_j <= fc_maxoff; fc_j = OffsetNumberNext(fc_j))
	{
		fc_array[fc_j].index = fc_j;
		fc_lu = GETENTRY(fc_entryvec, fc_j); /* 用作临时值 */
		fc_array[fc_j].r = LTG_GETLNODE(fc_lu, fc_siglen);
	}

	qsort((void *) &fc_array[FirstOffsetNumber], fc_maxoff - FirstOffsetNumber + 1,
		  sizeof(RIX), fc_treekey_cmp);

	fc_lu_l = fc_lu_r = fc_ru_l = fc_ru_r = NULL;
	for (fc_j = FirstOffsetNumber; fc_j <= fc_maxoff; fc_j = OffsetNumberNext(fc_j))
	{
		fc_lu = GETENTRY(fc_entryvec, fc_array[fc_j].index);	/* 用作临时值 */
		if (fc_j <= (fc_maxoff - FirstOffsetNumber + 1) / 2)
		{
			fc_v->spl_left[fc_v->spl_nleft] = fc_array[fc_j].index;
			fc_v->spl_nleft++;
			if (fc_lu_r == NULL || ltree_compare(LTG_GETRNODE(fc_lu, fc_siglen), fc_lu_r) > 0)
				fc_lu_r = LTG_GETRNODE(fc_lu, fc_siglen);
			if (LTG_ISONENODE(fc_lu))
				fc_hashing(fc_ls, LTG_NODE(fc_lu), fc_siglen);
			else
			{
				if (fc_lisat || LTG_ISALLTRUE(fc_lu))
					fc_lisat = true;
				else
				{
					BITVECP		fc_sc = LTG_SIGN(fc_lu);

					LOOPBYTE(fc_siglen)
						((unsigned char *) fc_ls)[i] |= fc_sc[i];
				}
			}
		}
		else
		{
			fc_v->spl_right[fc_v->spl_nright] = fc_array[fc_j].index;
			fc_v->spl_nright++;
			if (fc_ru_r == NULL || ltree_compare(LTG_GETRNODE(fc_lu, fc_siglen), fc_ru_r) > 0)
				fc_ru_r = LTG_GETRNODE(fc_lu, fc_siglen);
			if (LTG_ISONENODE(fc_lu))
				fc_hashing(fc_rs, LTG_NODE(fc_lu), fc_siglen);
			else
			{
				if (fc_risat || LTG_ISALLTRUE(fc_lu))
					fc_risat = true;
				else
				{
					BITVECP		fc_sc = LTG_SIGN(fc_lu);

					LOOPBYTE(fc_siglen)
						((unsigned char *) fc_rs)[i] |= fc_sc[i];
				}
			}
		}
	}

	if (fc_lisat == false)
	{
		fc_lisat = true;
		LOOPBYTE(fc_siglen)
		{
			if (((unsigned char *) fc_ls)[i] != 0xff)
			{
				fc_lisat = false;
				break;
			}
		}
	}

	if (fc_risat == false)
	{
		fc_risat = true;
		LOOPBYTE(fc_siglen)
		{
			if (((unsigned char *) fc_rs)[i] != 0xff)
			{
				fc_risat = false;
				break;
			}
		}
	}

	fc_lu_l = LTG_GETLNODE(GETENTRY(fc_entryvec, fc_array[FirstOffsetNumber].index), fc_siglen);
	fc_lu = ltree_gist_alloc(fc_lisat, fc_ls, fc_siglen, fc_lu_l, fc_lu_r);

	fc_ru_l = LTG_GETLNODE(GETENTRY(fc_entryvec, fc_array[1 + ((fc_maxoff - FirstOffsetNumber + 1) / 2)].index), fc_siglen);
	fc_ru = ltree_gist_alloc(fc_risat, fc_rs, fc_siglen, fc_ru_l, fc_ru_r);

	pfree(fc_ls);
	pfree(fc_rs);

	fc_v->spl_ldatum = PointerGetDatum(fc_lu);
	fc_v->spl_rdatum = PointerGetDatum(fc_ru);

	PG_RETURN_POINTER(fc_v);
}

static bool fc_gist_isparent(ltree_gist *fc_key, ltree *fc_query, int fc_siglen)
{
	int32		fc_numlevel = fc_query->numlevel;
	int			fc_i;

	for (fc_i = fc_query->numlevel; fc_i >= 0; fc_i--)
	{
		fc_query->numlevel = fc_i;
		if (ltree_compare(fc_query, LTG_GETLNODE(fc_key, fc_siglen)) >= 0 &&
			ltree_compare(fc_query, LTG_GETRNODE(fc_key, fc_siglen)) <= 0)
		{
			fc_query->numlevel = fc_numlevel;
			return true;
		}
	}

	fc_query->numlevel = fc_numlevel;
	return false;
}

static ltree * fc_copy_ltree(ltree *fc_src)
{
	ltree	   *fc_dst = (ltree *) palloc0(VARSIZE(fc_src));

	memcpy(fc_dst, fc_src, VARSIZE(fc_src));
	return fc_dst;
}

static bool fc_gist_ischild(ltree_gist *fc_key, ltree *fc_query, int fc_siglen)
{
	ltree	   *fc_left = fc_copy_ltree(LTG_GETLNODE(fc_key, fc_siglen));
	ltree	   *fc_right = fc_copy_ltree(LTG_GETRNODE(fc_key, fc_siglen));
	bool		fc_res = true;

	if (fc_left->numlevel > fc_query->numlevel)
		fc_left->numlevel = fc_query->numlevel;

	if (ltree_compare(fc_query, fc_left) < 0)
		fc_res = false;

	if (fc_right->numlevel > fc_query->numlevel)
		fc_right->numlevel = fc_query->numlevel;

	if (fc_res && ltree_compare(fc_query, fc_right) > 0)
		fc_res = false;

	pfree(fc_left);
	pfree(fc_right);

	return fc_res;
}

static bool fc_gist_qe(ltree_gist *fc_key, lquery *fc_query, int fc_siglen)
{
	lquery_level *fc_curq = LQUERY_FIRST(fc_query);
	BITVECP		fc_sign = LTG_SIGN(fc_key);
	int			fc_qlen = fc_query->numlevel;

	if (LTG_ISALLTRUE(fc_key))
		return true;

	while (fc_qlen > 0)
	{
		if (fc_curq->numvar && LQL_CANLOOKSIGN(fc_curq))
		{
			bool		fc_isexist = false;
			int			fc_vlen = fc_curq->numvar;
			lquery_variant *fc_curv = LQL_FIRST(fc_curq);

			while (fc_vlen > 0)
			{
				if (GETBIT(fc_sign, HASHVAL(fc_curv->val, fc_siglen)))
				{
					fc_isexist = true;
					break;
				}
				fc_curv = LVAR_NEXT(fc_curv);
				fc_vlen--;
			}
			if (!fc_isexist)
				return false;
		}

		fc_curq = LQL_NEXT(fc_curq);
		fc_qlen--;
	}

	return true;
}

static int fc_gist_tqcmp(ltree *fc_t, lquery *fc_q)
{
	ltree_level *fc_al = LTREE_FIRST(fc_t);
	lquery_level *fc_ql = LQUERY_FIRST(fc_q);
	lquery_variant *fc_bl;
	int			fc_an = fc_t->numlevel;
	int			fc_bn = fc_q->firstgood;
	int			fc_res = 0;

	while (fc_an > 0 && fc_bn > 0)
	{
		fc_bl = LQL_FIRST(fc_ql);
		if ((fc_res = memcmp(fc_al->name, fc_bl->name, Min(fc_al->len, fc_bl->len))) == 0)
		{
			if (fc_al->len != fc_bl->len)
				return fc_al->len - fc_bl->len;
		}
		else
			return fc_res;
		fc_an--;
		fc_bn--;
		fc_al = LEVEL_NEXT(fc_al);
		fc_ql = LQL_NEXT(fc_ql);
	}

	return Min(fc_t->numlevel, fc_q->firstgood) - fc_q->firstgood;
}

static bool fc_gist_between(ltree_gist *fc_key, lquery *fc_query, int fc_siglen)
{
	if (fc_query->firstgood == 0)
		return true;

	if (fc_gist_tqcmp(LTG_GETLNODE(fc_key, fc_siglen), fc_query) > 0)
		return false;

	if (fc_gist_tqcmp(LTG_GETRNODE(fc_key, fc_siglen), fc_query) < 0)
		return false;

	return true;
}

typedef struct LtreeSignature
{
	BITVECP		sign;
	int			siglen;
} LtreeSignature;

static bool fc_checkcondition_bit(void *fc_cxt, ITEM *fc_val)
{
	LtreeSignature *fc_sig = fc_cxt;

	return (FLG_CANLOOKSIGN(fc_val->flag)) ? GETBIT(fc_sig->sign, HASHVAL(fc_val->val, fc_sig->siglen)) : true;
}

static bool fc_gist_qtxt(ltree_gist *fc_key, ltxtquery *fc_query, int fc_siglen)
{
	LtreeSignature fc_sig;

	if (LTG_ISALLTRUE(fc_key))
		return true;

	fc_sig.sign = LTG_SIGN(fc_key);
	fc_sig.siglen = fc_siglen;

	return ltree_execute(GETQUERY(fc_query),
						 &fc_sig, false,
						 fc_checkcondition_bit);
}

static bool fc_arrq_cons(ltree_gist *fc_key, ArrayType *fc__query, int fc_siglen)
{
	lquery	   *fc_query = (lquery *) ARR_DATA_PTR(fc__query);
	int			fc_num = ArrayGetNItems(ARR_NDIM(fc__query), ARR_DIMS(fc__query));

	if (ARR_NDIM(fc__query) > 1)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
				 errmsg("array must be one-dimensional")));
	if (array_contains_nulls(fc__query))
		ereport(ERROR,
				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
				 errmsg("array must not contain nulls")));

	while (fc_num > 0)
	{
		if (fc_gist_qe(fc_key, fc_query, fc_siglen) && fc_gist_between(fc_key, fc_query, fc_siglen))
			return true;
		fc_num--;
		fc_query = NEXTVAL(fc_query);
	}
	return false;
}

Datum ltree_consistent(PG_FUNCTION_ARGS)
{
	GISTENTRY  *fc_entry = (GISTENTRY *) PG_GETARG_POINTER(0);
	StrategyNumber fc_strategy = (StrategyNumber) PG_GETARG_UINT16(2);

	/* Oid		subtype = PG_GETARG_OID(3); */
	bool	   *fc_recheck = (bool *) PG_GETARG_POINTER(4);
	int			fc_siglen = LTREE_GET_SIGLEN();
	ltree_gist *fc_key = (ltree_gist *) DatumGetPointer(fc_entry->key);
	void	   *fc_query = NULL;
	bool		fc_res = false;

	/* 由此函数服务的所有案例都是精确的 */
	*fc_recheck = false;

	switch (fc_strategy)
	{
		case BTLessStrategyNumber:
			fc_query = PG_GETARG_LTREE_P(1);
			fc_res = (GIST_LEAF(fc_entry)) ?
				(ltree_compare((ltree *) fc_query, LTG_NODE(fc_key)) > 0)
				:
				(ltree_compare((ltree *) fc_query, LTG_GETLNODE(fc_key, fc_siglen)) >= 0);
			break;
		case BTLessEqualStrategyNumber:
			fc_query = PG_GETARG_LTREE_P(1);
			fc_res = (ltree_compare((ltree *) fc_query, LTG_GETLNODE(fc_key, fc_siglen)) >= 0);
			break;
		case BTEqualStrategyNumber:
			fc_query = PG_GETARG_LTREE_P(1);
			if (GIST_LEAF(fc_entry))
				fc_res = (ltree_compare((ltree *) fc_query, LTG_NODE(fc_key)) == 0);
			else
				fc_res = (ltree_compare((ltree *) fc_query, LTG_GETLNODE(fc_key, fc_siglen)) >= 0
					   &&
					   ltree_compare((ltree *) fc_query, LTG_GETRNODE(fc_key, fc_siglen)) <= 0);
			break;
		case BTGreaterEqualStrategyNumber:
			fc_query = PG_GETARG_LTREE_P(1);
			fc_res = (ltree_compare((ltree *) fc_query, LTG_GETRNODE(fc_key, fc_siglen)) <= 0);
			break;
		case BTGreaterStrategyNumber:
			fc_query = PG_GETARG_LTREE_P(1);
			fc_res = (GIST_LEAF(fc_entry)) ?
				(ltree_compare((ltree *) fc_query, LTG_GETRNODE(fc_key, fc_siglen)) < 0)
				:
				(ltree_compare((ltree *) fc_query, LTG_GETRNODE(fc_key, fc_siglen)) <= 0);
			break;
		case 10:
			fc_query = PG_GETARG_LTREE_P_COPY(1);
			fc_res = (GIST_LEAF(fc_entry)) ?
				inner_isparent((ltree *) fc_query, LTG_NODE(fc_key))
				:
				fc_gist_isparent(fc_key, (ltree *) fc_query, fc_siglen);
			break;
		case 11:
			fc_query = PG_GETARG_LTREE_P(1);
			fc_res = (GIST_LEAF(fc_entry)) ?
				inner_isparent(LTG_NODE(fc_key), (ltree *) fc_query)
				:
				fc_gist_ischild(fc_key, (ltree *) fc_query, fc_siglen);
			break;
		case 12:
		case 13:
			fc_query = PG_GETARG_LQUERY_P(1);
			if (GIST_LEAF(fc_entry))
				fc_res = DatumGetBool(DirectFunctionCall2(ltq_regex,
													   PointerGetDatum(LTG_NODE(fc_key)),
													   PointerGetDatum((lquery *) fc_query)
													   ));
			else
				fc_res = (fc_gist_qe(fc_key, (lquery *) fc_query, fc_siglen) &&
					   fc_gist_between(fc_key, (lquery *) fc_query, fc_siglen));
			break;
		case 14:
		case 15:
			fc_query = PG_GETARG_LTXTQUERY_P(1);
			if (GIST_LEAF(fc_entry))
				fc_res = DatumGetBool(DirectFunctionCall2(ltxtq_exec,
													   PointerGetDatum(LTG_NODE(fc_key)),
													   PointerGetDatum((ltxtquery *) fc_query)
													   ));
			else
				fc_res = fc_gist_qtxt(fc_key, (ltxtquery *) fc_query, fc_siglen);
			break;
		case 16:
		case 17:
			fc_query = PG_GETARG_ARRAYTYPE_P(1);
			if (GIST_LEAF(fc_entry))
				fc_res = DatumGetBool(DirectFunctionCall2(lt_q_regex,
													   PointerGetDatum(LTG_NODE(fc_key)),
													   PointerGetDatum((ArrayType *) fc_query)
													   ));
			else
				fc_res = fc_arrq_cons(fc_key, (ArrayType *) fc_query, fc_siglen);
			break;
		default:
			/* 内部错误 */
			elog(ERROR, "unrecognized StrategyNumber: %d", fc_strategy);
	}

	PG_FREE_IF_COPY(fc_query, 1);
	PG_RETURN_BOOL(fc_res);
}

static void fc_ltree_gist_relopts_validator(void *fc_parsed_options, relopt_value *fc_vals,
							 int fc_nvals)
{
	LtreeGistOptions *fc_options = (LtreeGistOptions *) fc_parsed_options;

	if (fc_options->siglen != INTALIGN(fc_options->siglen))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("siglen value must be a multiple of %d", ALIGNOF_INT)));
}

Datum ltree_gist_options(PG_FUNCTION_ARGS)
{
	local_relopts *fc_relopts = (local_relopts *) PG_GETARG_POINTER(0);

	init_local_reloptions(fc_relopts, sizeof(LtreeGistOptions));
	add_local_int_reloption(fc_relopts, "siglen",
							"signature length in bytes",
							LTREE_SIGLEN_DEFAULT,
							INTALIGN(1),
							LTREE_SIGLEN_MAX,
							offsetof(LtreeGistOptions, siglen));
	register_reloptions_validator(fc_relopts, fc_ltree_gist_relopts_validator);

	PG_RETURN_VOID();
}
